import csv
import queue
import re
import socket
import threading
import time
import tkinter as tk
from datetime import datetime
from tkinter import ttk, scrolledtext, filedialog, messagebox

import ttkbootstrap as ttkb
from tinydb import TinyDB
from ttkbootstrap.constants import *

from app.util.annotation_util import annotate, ClassInfo
from app.view.base_view import View

# 初始化数据库并定义表格
db = TinyDB('config/au_mac.json', ensure_ascii=False, indent=4)
config_table = db.table('config')
records_table = db.table('records')


@annotate(ClassInfo(name="FtTcpBleHwAu", category="ft_tool"))
class FtTcpBleHwAulView(View):
    def __init__(self, parent, controller):
        super().__init__(parent, controller)

        # 服务器状态
        self.server_running = False
        self.server_socket = None
        self.client_socket = None
        self.client_address = None
        self.device_id = None
        self.current_mac = None
        self.test_in_progress = False
        self.test_aborted = False
        self.receive_queue = queue.Queue()

        # 加载配置
        self.load_config()

        # 创建界面
        self._create_widgets()

        # 启动接收处理线程
        self.receive_thread = threading.Thread(target=self.process_received_data, daemon=True)
        self.receive_thread.start()

    def load_config(self):
        """加载保存的配置"""
        configs = config_table.all()
        if configs:
            self.last_config = configs[0]
            self.last_ip = self.last_config.get('ip', '0.0.0.0')
            self.last_port = self.last_config.get('port', 8080)
        else:
            self.last_ip = '0.0.0.0'
            self.last_port = 8080

    def save_config(self):
        """保存配置到数据库"""
        config_table.truncate()
        config_table.insert({
            'ip': self.ip_var.get(),
            'port': self.port_var.get(),
            'timestamp': datetime.now().isoformat()
        })

    def _create_widgets(self):
        """创建界面组件"""
        # 主框架
        main_frame = ttk.Frame(self, padding=10)
        main_frame.pack(fill=BOTH, expand=YES)

        # 顶部服务器配置区域
        server_frame = ttk.LabelFrame(main_frame, text="服务器配置", padding=10)
        server_frame.pack(fill=X, pady=(0, 10))

        # IP选择
        ttk.Label(server_frame, text="本地IP:").grid(row=0, column=0, padx=5, pady=5, sticky=W)
        self.ip_var = ttkb.StringVar(value=self.last_ip)
        self.ip_combo = ttk.Combobox(server_frame, textvariable=self.ip_var, width=15)
        self.ip_combo['values'] = self.get_local_ips()
        self.ip_combo.grid(row=0, column=1, padx=5, pady=5, sticky=W)

        # 端口输入
        ttk.Label(server_frame, text="端口:").grid(row=0, column=2, padx=5, pady=5, sticky=W)
        self.port_var = ttkb.IntVar(value=self.last_port)
        port_entry = ttk.Entry(server_frame, textvariable=self.port_var, width=8)
        port_entry.grid(row=0, column=3, padx=5, pady=5, sticky=W)

        # 服务器控制按钮
        self.start_stop_btn = ttkb.Button(
            server_frame, text="启动服务", command=self.toggle_server,
            bootstyle=SUCCESS
        )
        self.start_stop_btn.grid(row=0, column=4, padx=10, pady=5)

        # 设备ID显示
        ttk.Label(server_frame, text="设备ID:").grid(row=0, column=5, padx=5, pady=5, sticky=W)
        self.device_id_var = ttkb.StringVar(value="")
        ttk.Label(server_frame, textvariable=self.device_id_var, font=('Helvetica', 10, 'bold')).grid(
            row=0, column=6, padx=5, pady=5, sticky=W
        )

        # 中间操作区域
        operation_frame = ttk.LabelFrame(main_frame, text="操作区", padding=10)
        operation_frame.pack(fill=X, pady=(0, 10))

        # MAC输入
        ttk.Label(operation_frame, text="MAC地址:").grid(row=0, column=0, padx=5, pady=5, sticky=W)
        self.mac_var = ttkb.StringVar()
        mac_entry = ttk.Entry(operation_frame, textvariable=self.mac_var, width=20, font=('Helvetica', 12))
        mac_entry.grid(row=0, column=1, padx=5, pady=5, sticky=W)
        mac_entry.bind('<Return>', self.on_mac_entered)
        mac_entry.focus_set()  # 设置焦点，方便扫码枪输入

        # 测试控制按钮
        self.start_test_btn = ttk.Button(
            operation_frame, text="开始测试", command=self.start_test,
            bootstyle=PRIMARY, state=DISABLED
        )
        self.start_test_btn.grid(row=0, column=2, padx=10, pady=5)

        self.abort_test_btn = ttk.Button(
            operation_frame, text="中断测试", command=self.abort_test,
            bootstyle=DANGER, state=DISABLED
        )
        self.abort_test_btn.grid(row=0, column=3, padx=10, pady=5)

        self.retest_btn = ttk.Button(
            operation_frame, text="重新测试", command=self.restart_test,
            bootstyle=INFO, state=DISABLED
        )
        self.retest_btn.grid(row=0, column=4, padx=10, pady=5)

        # 记录导出按钮
        self.export_records_btn = ttk.Button(
            operation_frame, text="导出记录", command=self.export_records,
            bootstyle=SECONDARY
        )
        self.export_records_btn.grid(row=0, column=5, padx=10, pady=5)

        # 日志导出按钮
        self.export_log_btn = ttk.Button(
            operation_frame, text="导出日志", command=self.export_log,
            bootstyle=SECONDARY
        )
        self.export_log_btn.grid(row=0, column=6, padx=10, pady=5)

        # 底部区域 - 日志、记录和MAC列表
        bottom_frame = ttk.Frame(main_frame)
        bottom_frame.pack(fill=BOTH, expand=YES)

        # 创建标签页
        notebook = ttk.Notebook(bottom_frame)
        notebook.pack(fill=BOTH, expand=YES, pady=5)

        # 日志标签页
        log_frame = ttk.Frame(notebook)
        notebook.add(log_frame, text="操作日志")

        self.log_text = scrolledtext.ScrolledText(log_frame, wrap=tk.WORD, height=15)
        self.log_text.pack(fill=BOTH, expand=YES, padx=5, pady=5)
        self.log_text.config(state=DISABLED)

        # 记录标签页
        records_frame = ttk.Frame(notebook)
        notebook.add(records_frame, text="测试记录")

        # 记录表格
        columns = ("timestamp", "device_id", "mac", "status", "message")
        self.records_tree = ttk.Treeview(records_frame, columns=columns, show="headings")

        # 设置列标题
        self.records_tree.heading("timestamp", text="时间")
        self.records_tree.heading("device_id", text="设备ID")
        self.records_tree.heading("mac", text="MAC地址")
        self.records_tree.heading("status", text="状态")
        self.records_tree.heading("message", text="信息")

        # 设置列宽
        self.records_tree.column("timestamp", width=140)
        self.records_tree.column("device_id", width=100)
        self.records_tree.column("mac", width=140)
        self.records_tree.column("status", width=80)
        self.records_tree.column("message", width=300)

        # 添加滚动条
        scrollbar = ttk.Scrollbar(records_frame, orient=VERTICAL, command=self.records_tree.yview)
        self.records_tree.configure(yscroll=scrollbar.set)

        # 布局
        self.records_tree.pack(side=LEFT, fill=BOTH, expand=YES, padx=5, pady=5)
        scrollbar.pack(side=RIGHT, fill=Y, pady=5)

        # MAC列表标签页 - 新增
        mac_list_frame = ttk.Frame(notebook)
        notebook.add(mac_list_frame, text="已测试MAC列表")

        # MAC列表表格
        mac_columns = ("timestamp", "device_id", "mac", "status")
        self.mac_tree = ttk.Treeview(mac_list_frame, columns=mac_columns, show="headings")

        # 设置列标题
        self.mac_tree.heading("timestamp", text="测试时间")
        self.mac_tree.heading("device_id", text="设备ID")
        self.mac_tree.heading("mac", text="MAC地址")
        self.mac_tree.heading("status", text="状态")

        # 设置列宽
        self.mac_tree.column("timestamp", width=160)
        self.mac_tree.column("device_id", width=120)
        self.mac_tree.column("mac", width=160)
        self.mac_tree.column("status", width=100)

        # 添加滚动条
        mac_scrollbar = ttk.Scrollbar(mac_list_frame, orient=VERTICAL, command=self.mac_tree.yview)
        self.mac_tree.configure(yscroll=mac_scrollbar.set)

        # 布局
        self.mac_tree.pack(side=LEFT, fill=BOTH, expand=YES, padx=5, pady=5)
        mac_scrollbar.pack(side=RIGHT, fill=Y, pady=5)

        # 加载记录和MAC列表
        self.load_records()
        self.load_mac_list()

    def get_local_ips(self):
        """获取本地IP地址列表"""
        ips = ['0.0.0.0']  # 允许绑定到所有接口
        try:
            # 创建临时socket来获取本地IP
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.connect(("8.8.8.8", 80))
            local_ip = s.getsockname()[0]
            ips.append(local_ip)
            s.close()
        except Exception as e:
            self.log(f"获取本地IP失败: {str(e)}")
        return ips

    def toggle_server(self):
        """启动或停止服务器"""
        if not self.server_running:
            self.start_server()
        else:
            self.stop_server()

    def start_server(self):
        """启动TCP服务器"""
        try:
            ip = self.ip_var.get()
            port = self.port_var.get()

            # 验证端口
            if not (1 <= port <= 65535):
                messagebox.showerror("错误", "端口必须在1-65535之间")
                return

            # 创建服务器socket
            self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.server_socket.bind((ip, port))
            self.server_socket.listen(1)

            self.server_running = True
            self.start_stop_btn.config(text="停止服务", bootstyle=DANGER)
            self.log(f"服务器已启动，监听 {ip}:{port}")

            # 保存配置
            self.save_config()

            # 启动接受连接的线程
            self.accept_thread = threading.Thread(target=self.accept_connections, daemon=True)
            self.accept_thread.start()

        except Exception as e:
            self.log(f"启动服务器失败: {str(e)}", level="error")
            messagebox.showerror("错误", f"启动服务器失败: {str(e)}")

    def stop_server(self):
        """停止TCP服务器"""
        if self.server_running:
            self.server_running = False

            # 关闭客户端连接
            if self.client_socket:
                try:
                    self.client_socket.close()
                    self.log(f"已断开与 {self.client_address} 的连接")
                except Exception as e:
                    self.log(f"关闭客户端连接失败: {str(e)}", level="error")
                self.client_socket = None
                self.client_address = None
                self.device_id = None
                self.device_id_var.set("")
                self.update_test_buttons(state=DISABLED)

            # 关闭服务器socket
            if self.server_socket:
                try:
                    self.server_socket.close()
                except Exception as e:
                    self.log(f"关闭服务器失败: {str(e)}", level="error")
                self.server_socket = None

            self.start_stop_btn.config(text="启动服务", bootstyle=SUCCESS)
            self.log("服务器已停止")

    def accept_connections(self):
        """接受客户端连接的线程函数"""
        while self.server_running:
            try:
                # 设置超时，以便能响应服务器停止命令
                self.server_socket.settimeout(1.0)
                client_socket, client_address = self.server_socket.accept()

                # 如果已有连接，先关闭
                if self.client_socket:
                    try:
                        self.client_socket.close()
                        self.log(f"已断开与 {self.client_address} 的旧连接")
                    except Exception as e:
                        self.log(f"关闭旧连接失败: {str(e)}", level="error")

                self.client_socket = client_socket
                self.client_address = client_address
                self.log(f"新连接: {client_address}")

                # 询问设备ID
                self.ask_device_id()

                # 启动接收数据的线程
                self.receive_data_thread = threading.Thread(target=self.receive_data, daemon=True)
                self.receive_data_thread.start()

            except socket.timeout:
                continue  # 超时，继续循环检查服务器是否仍在运行
            except Exception as e:
                if self.server_running:  # 只有在服务器仍在运行时才记录错误
                    self.log(f"接受连接失败: {str(e)}", level="error")
                break

    def ask_device_id(self):
        """询问设备ID"""
        if self.server_running:
            command = "6804008109ffffffff5eaf16"
            self.client_socket.sendall(bytes.fromhex(command))


    def receive_data(self):
        """接收客户端数据的线程函数"""
        while self.server_running and self.client_socket:
            try:
                data = self.client_socket.recv(1024)
                if not data:
                    self.log(f"客户端 {self.client_address} 已断开连接")
                    self.client_socket.close()
                    self.client_socket = None
                    self.client_address = None
                    self.device_id = None
                    self.update_test_buttons(state=DISABLED)
                    break

                if len(data) == 12:
                    device_id = f"{data[5:9].hex().upper()}"
                    self.device_id_var.set(f"{device_id}")

                # message = data.decode('utf-8').strip()
                self.log(f"收到数据: {data.hex().upper()}")
                self.receive_queue.put(data)

            except Exception as e:
                if self.server_running and self.client_socket:
                    self.log(f"接收数据错误: {str(e)}", level="error")
                break

    def process_received_data(self):
        """处理接收到的数据的线程函数"""
        while True:
            try:
                # 阻塞等待数据
                message = self.receive_queue.get(block=True, timeout=1)
                self.receive_queue.task_done()
            except queue.Empty:
                continue
            except Exception as e:
                self.log(f"处理接收数据错误: {str(e)}", level="error")

    def send_data(self, data):
        """向客户端发送数据"""
        if not self.server_running or not self.client_socket:
            self.log("未连接到客户端，无法发送数据", level="error")
            return False

        try:
            self.client_socket.sendall(f"{data}\r\n".encode('utf-8'))
            self.log(f"发送数据: {data}")
            return True
        except Exception as e:
            self.log(f"发送数据失败: {str(e)}", level="error")
            return False

    def on_mac_entered(self, event=None):
        """处理MAC地址输入完成"""
        mac = self.mac_var.get().strip()
        if mac:
            # 简单验证MAC地址格式
            if re.match(r'^[0-9A-Fa-f:]{17}|[0-9A-Fa-f]{12}$', mac):
                # 标准化MAC地址格式，去除冒号
                self.current_mac = mac.replace(':', '').upper()
                self.log(f"已输入MAC地址: {self.current_mac}")
                return True
            else:
                self.log("MAC地址格式无效，请重新输入", level="error")
                messagebox.showerror("错误", "MAC地址格式无效，请使用类似001122AABBCC的格式")
        return False

    def update_test_buttons(self, state):
        """更新测试按钮状态"""
        self.start_test_btn.config(state=state)
        if state == NORMAL:
            self.retest_btn.config(state=state)
        else:
            self.retest_btn.config(state=DISABLED)

    def start_test(self):
        """开始测试流程"""
        if not self.on_mac_entered():
            return

        if self.test_in_progress:
            messagebox.showinfo("提示", "测试正在进行中")
            return

        self.test_in_progress = True
        self.test_aborted = False
        self.start_test_btn.config(state=DISABLED)
        self.abort_test_btn.config(state=NORMAL)
        self.retest_btn.config(state=DISABLED)

        # 启动测试线程
        self.test_thread = threading.Thread(target=self.test_process, daemon=True)
        self.test_thread.start()

    def abort_test(self):
        """中断当前测试"""
        if not self.test_in_progress:
            return

        self.test_aborted = True
        self.log("正在中断测试...")
        self.abort_test_btn.config(state=DISABLED)

    def restart_test(self):
        """重新测试"""
        self.start_test()

    def test_process(self):
        """测试流程的主要逻辑"""
        result = False
        message = ""
        mac = self.current_mac

        try:
            self.log("开始测试流程...")

            if self.test_aborted:
                raise Exception("测试已被用户中断")

            # 1. 发送+++进入命令模式
            self.log("发送命令: +++ (进入命令模式)")
            if not self.send_data("+++"):
                raise Exception("发送+++失败")

            # 等待响应
            time.sleep(1)
            if not self.check_response("OK", 3):
                raise Exception("进入命令模式失败")

            if self.test_aborted:
                raise Exception("测试已被用户中断")

            # 2. 发送AT+CONN连接到设备，最多尝试5次
            connected = False
            for attempt in range(5):
                self.log(f"连接尝试 {attempt + 1}/5: AT+CONN={mac}")
                if not self.send_data(f"AT+CONN={mac}"):
                    time.sleep(2)
                    continue

                # 等待响应
                connected = self.check_response("OK", 3)
                if connected:
                    self.log("连接成功")
                    break
                else:
                    self.log("连接失败，尝试断开重连")
                    self.send_data("AT+DISCON")
                    time.sleep(1)

            if not connected:
                raise Exception("连接设备失败，已尝试5次")

            if self.test_aborted:
                raise Exception("测试已被用户中断")

            # 3. 发送AT+SEND=AT+MAC=MAC地址
            self.log(f"发送命令: AT+SEND=AT+MAC={mac}")
            if not self.send_data(f"AT+SEND=AT+MAC={mac}"):
                raise Exception("发送AT+MAC命令失败")

            # 等待响应
            ok_received = self.check_response("OK", 3)
            if not ok_received:
                raise Exception("未收到OK响应")

            if self.test_aborted:
                raise Exception("测试已被用户中断")

            # 4. 发送AT+SEND=AT+MAC?查询MAC
            self.log("发送命令: AT+SEND=AT+MAC?")
            if not self.send_data("AT+SEND=AT+MAC?"):
                raise Exception("发送AT+MAC?命令失败")

            # 等待响应
            mac_matched = self.check_response(mac, 3)
            if not mac_matched:
                raise Exception("查询到的MAC地址不匹配")

            if self.test_aborted:
                raise Exception("测试已被用户中断")

            # 5. 发送AT+DISCON断开连接
            self.log("发送命令: AT+DISCON (断开连接)")
            if not self.send_data("AT+DISCON"):
                raise Exception("发送AT+DISCON命令失败")

            # 等待响应
            if not self.check_response("OK", 3):
                raise Exception("断开连接未收到OK响应")

            if self.test_aborted:
                raise Exception("测试已被用户中断")

            # 6. 发送AT+RESET重启设备
            self.log("发送命令: AT+RESET (重启设备)")
            if not self.send_data("AT+RESET"):
                raise Exception("发送AT+RESET命令失败")

            # 等待响应
            if not self.check_response("OK", 3):
                raise Exception("重启设备未收到OK响应")

            # 所有步骤完成
            result = True
            message = "测试成功完成"
            self.log(message)

        except Exception as e:
            message = f"测试失败: {str(e)}"
            self.log(message, level="error")
        finally:
            self.test_in_progress = False
            self.after(0, lambda: self._test_complete(result, message, mac))

    def check_response(self, expected, timeout=3):
        """检查是否收到预期的响应"""
        start_time = time.time()
        while time.time() - start_time < timeout:
            try:
                # 检查队列中是否有预期的响应
                if not self.receive_queue.empty():
                    response = self.receive_queue.get_nowait()
                    if expected in response:
                        return True
                time.sleep(0.1)
            except Exception as e:
                self.log(f"检查响应错误: {str(e)}", level="error")
                return False
        return False

    def _test_complete(self, result, message, mac):
        """测试完成后的处理（在主线程中执行）"""
        # 更新按钮状态
        self.start_test_btn.config(state=NORMAL)
        self.abort_test_btn.config(state=DISABLED)
        self.retest_btn.config(state=NORMAL)

        # 记录测试结果
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        status = "成功" if result else "失败"

        # 保存到数据库
        records_table.insert({
            'timestamp': timestamp,
            'device_id': self.device_id,
            'mac': mac,
            'status': status,
            'message': message
        })

        # 更新表格显示
        self.add_record_to_table(timestamp, self.device_id, mac, status, message)
        self.add_mac_to_list(timestamp, self.device_id, mac, status)

        # 显示结果消息
        if result:
            messagebox.showinfo("测试结果", "测试成功完成")
        else:
            messagebox.showerror("测试结果", message)

    def log(self, message, level="info"):
        """记录日志"""
        timestamp = datetime.now().strftime("%H:%M:%S")
        log_entry = f"[{timestamp}] {message}\n"

        # 在主线程中更新日志
        def update_log():
            self.log_text.config(state=NORMAL)
            self.log_text.insert(END, log_entry)
            self.log_text.see(END)
            self.log_text.config(state=DISABLED)

        self.after(0, update_log)

    def load_records(self):
        """加载记录到表格"""
        # 清空现有记录
        for item in self.records_tree.get_children():
            self.records_tree.delete(item)

        # 从数据库加载记录，按时间倒序排列
        records = sorted(records_table.all(), key=lambda x: x['timestamp'], reverse=True)

        for record in records:
            self.add_record_to_table(
                record['timestamp'],
                record['device_id'],
                record['mac'],
                record['status'],
                record['message']
            )

    def add_record_to_table(self, timestamp, device_id, mac, status, message):
        """添加记录到表格"""

        # 在主线程中更新表格
        def update_table():
            # 根据状态设置行颜色
            tag = "success" if status == "成功" else "error"
            self.records_tree.insert("", 0, values=(timestamp, device_id, mac, status, message), tags=(tag,))

        self.after(0, update_table)

        # 配置标签样式
        self.records_tree.tag_configure("success", background="#e6ffe6")
        self.records_tree.tag_configure("error", background="#ffe6e6")

    def load_mac_list(self):
        """加载已测试MAC列表"""
        # 清空现有记录
        for item in self.mac_tree.get_children():
            self.mac_tree.delete(item)

        # 从数据库加载记录，按时间倒序排列
        records = sorted(records_table.all(), key=lambda x: x['timestamp'], reverse=True)

        for record in records:
            self.add_mac_to_list(
                record['timestamp'],
                record['device_id'],
                record['mac'],
                record['status']
            )

    def add_mac_to_list(self, timestamp, device_id, mac, status):
        """添加MAC到列表"""

        # 在主线程中更新表格
        def update_list():
            # 根据状态设置行颜色
            tag = "success" if status == "成功" else "error"
            self.mac_tree.insert("", 0, values=(timestamp, device_id, mac, status), tags=(tag,))

        self.after(0, update_list)

        # 配置标签样式
        self.mac_tree.tag_configure("success", background="#e6ffe6")
        self.mac_tree.tag_configure("error", background="#ffe6e6")

    def export_records(self):
        """导出记录为CSV文件"""
        records = records_table.all()
        if not records:
            messagebox.showinfo("提示", "没有记录可导出")
            return

        file_path = filedialog.asksaveasfilename(
            defaultextension=".csv",
            filetypes=[("CSV文件", "*.csv"), ("所有文件", "*.*")],
            title="导出记录"
        )

        if not file_path:
            return

        try:
            with open(file_path, 'w', newline='', encoding='utf-8') as file:
                writer = csv.writer(file)
                # 写入表头
                writer.writerow(["时间", "设备ID", "MAC地址", "状态", "信息"])

                # 写入记录
                for record in sorted(records, key=lambda x: x['timestamp']):
                    writer.writerow([
                        record['timestamp'],
                        record['device_id'],
                        record['mac'],
                        record['status'],
                        record['message']
                    ])

            self.log(f"记录已导出到 {file_path}")
            messagebox.showinfo("成功", f"记录已导出到 {file_path}")
        except Exception as e:
            self.log(f"导出记录失败: {str(e)}", level="error")
            messagebox.showerror("错误", f"导出记录失败: {str(e)}")

    def export_log(self):
        """导出日志为文本文件"""
        log_content = self.log_text.get(1.0, END)
        if not log_content.strip():
            messagebox.showinfo("提示", "日志为空，无需导出")
            return

        file_path = filedialog.asksaveasfilename(
            defaultextension=".txt",
            filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")],
            title="导出日志"
        )

        if not file_path:
            return

        try:
            with open(file_path, 'w', encoding='utf-8') as file:
                file.write(log_content)

            self.log(f"日志已导出到 {file_path}")
            messagebox.showinfo("成功", f"日志已导出到 {file_path}")
        except Exception as e:
            self.log(f"导出日志失败: {str(e)}", level="error")
            messagebox.showerror("错误", f"导出日志失败: {str(e)}")

    def on_closing(self):
        """窗口关闭时的处理"""
        self.stop_server()
        self.destroy()


def create_widgets(self):
    pass
